package com.example.mysql_mybatis_demo.springsource.config;

import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * 自己实现一个BeanPostProcessor，注意 BeanPostProcessor本身也是一个特殊的Bean
 * BeanPostProcessor的两个方法都带有默认实现，因此需要手动添加Override重载方法
 *
 * 同时额外实现了一个Ordered接口，修改其实现方法 getOrder() 的返回值可以自定义多个BeanPostProcessor的执行顺序
 * 由于Ordered接口没什么技术含量，就不写两个BeanPostProcessor演示了
 */
@Component
public class BeanPostProcessorTest implements BeanPostProcessor, Ordered {
    /**
     * 看名字也能猜出来，这个方法会在Bean被实例化之前执行
     * 注意，所有的Bean都会遍历执行这个方法，我们需要手动对传入的Bean/BeanName进行判断，从而执行对应的任务
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        /**
         * 对所有执行初始化的Bean进行处理
         * 注意!!!经过之前的BeanFactoryPostProcessor操作，此时名为 beanPostProcessorTestBean 的bean，元类信息实际上是 beanPostProcessorTestSecBean
         */
        if(beanName.equals("beanPostProcessorTestBean")){
            // 对特定的Bean进行操作
            System.out.println("BeanPostProcessor命中" + beanName + ", 执行BeforeInitialization, 尝试在初始化前更改属性值");  // 这里打印仍然会说beanName是 beanPostProcessorTestBean
            //getFields()：获得某个类的所有的公共（public）的字段，包括父类中的字段。
            //getDeclaredFields()：获得某个类的所有声明的字段，即包括public、private和proteced，但是不包括父类的申明字段。
            Field[] fields = bean.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                //成员变量为private,故必须进行此操作
                //以通过反射获取私有变量的值，在访问时会忽略访问修饰符的检查
                fields[i].setAccessible(true);
                if (fields[i].getName().equals("name")){
                    // 对特定的属性字段进行操作
                    try {
                        System.out.println("执行BeforeInitialization前的Bean:" + bean);  // 但打印这个bean可以很明显看出，bean的元类已经是 beanPostProcessorTestSecBean了
                        fields[i].set(bean,"BeforeInitialization");
                        System.out.println("执行BeforeInitialization后的Bean:" + bean);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return bean;
    }

    /**
     * 该方法则会在Bean实例化之后执行，命中逻辑和上面的方法完全相同
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("beanPostProcessorTestBean")){
            System.out.println("BeanPostProcessor命中" + beanName + ", 执行AfterInitialization, 尝试在初始化后更改属性值");
            Field[] fields = bean.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                if (fields[i].getName().equals("name")){
                    try {
                        System.out.println("执行AfterInitialization前的Bean:" + bean);
                        fields[i].set(bean,"AfterInitialization");
                        System.out.println("执行AfterInitialization后的Bean:" + bean);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return bean;
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
